package heap;

import java.io.IOException;
import java.util.Iterator;

import bufmgr.BufMgrException;
import bufmgr.BufferPoolExceededException;
import bufmgr.HashEntryNotFoundException;
import bufmgr.HashOperationException;
import bufmgr.InvalidFrameNumberException;
import bufmgr.PageNotReadException;
import bufmgr.PagePinnedException;
import bufmgr.PageUnpinnedException;
import bufmgr.ReplacerException;

import global.GlobalConst;
import global.PageId;
import global.RID;
import global.SystemDefs;

public class Scan implements GlobalConst {
	private Heapfile hf;
	private HFPage cursor;
	private RID ridCursor;
	private Iterator<PageId> iter;

	/**
	 * The constructor pins the first directory page in the file and initializes
	 * its private data members from the private data member from hf
	 * 
	 * @param hf
	 *            A HeapFile object
	 * @throws InvalidTupleSizeException
	 * @throws IOException
	 * @throws ReplacerException
	 * @throws HashOperationException
	 * @throws PageUnpinnedException
	 * @throws InvalidFrameNumberException
	 * @throws PageNotReadException
	 * @throws BufferPoolExceededException
	 * @throws PagePinnedException
	 * @throws BufMgrException
	 * @throws HashEntryNotFoundException
	 */
	public Scan(Heapfile hf) throws InvalidTupleSizeException, IOException,
			ReplacerException, HashOperationException, PageUnpinnedException,
			InvalidFrameNumberException, PageNotReadException,
			BufferPoolExceededException, PagePinnedException, BufMgrException,
			HashEntryNotFoundException {
		this.hf = hf;
		iter = hf.iterator();
		PageId pid = iter.next();
		cursor = new HFPage();
		SystemDefs.JavabaseBM.pinPage(pid, cursor, false);
		ridCursor = cursor.firstRecord();
	}

	/**
	 * Retrieve the next record in a sequential scan
	 * 
	 * @param rid
	 *            Record ID of the record
	 * @return Tuple of the retrieved record.
	 * @throws InvalidSlotNumberException
	 * @throws HFException
	 * @throws HFDiskMgrException
	 * @throws HFBufMgrException
	 * @throws Exception
	 */
	public Tuple getNext(RID rid) throws InvalidSlotNumberException,
			HFException, HFDiskMgrException, HFBufMgrException, Exception {
		RID t = ridCursor;
		if (t != null) {
			rid.copyRid(t);
			ridCursor = cursor.nextRecord(ridCursor);
			return cursor.getRecord(t);
		}
		if (iter.hasNext()) {
			SystemDefs.JavabaseBM.unpinPage(cursor.getCurPage(), false);
			PageId pid = iter.next();
			SystemDefs.JavabaseBM.pinPage(pid, cursor, false);
			ridCursor = cursor.firstRecord();
			if (ridCursor != null) {
				rid.copyRid(ridCursor);
				ridCursor = cursor.nextRecord(ridCursor);
				return cursor.getRecord(rid);
			} else {
				SystemDefs.JavabaseBM.unpinPage(pid, false);
				return null;
			}
		}
		SystemDefs.JavabaseBM.unpinPage(cursor.getCurPage(), false);
		return null;
	}

	/**
	 * Position the scan cursor to the record with the given rid.
	 * 
	 * @param rid
	 * @return true if successful, false otherwise.
	 * @throws InvalidSlotNumberException
	 * @throws HFException
	 * @throws HFDiskMgrException
	 * @throws HFBufMgrException
	 * @throws Exception
	 */
	public boolean position(RID rid) throws InvalidSlotNumberException,
			HFException, HFDiskMgrException, HFBufMgrException, Exception {
		HFPage page = hf.getPage(rid);
		if (page == null) {
			return false;
		} else {
			cursor = page;
			ridCursor = rid;
			return true;
		}
	}

	/**
	 * Closes the Scan object
	 */
	public void closescan() {
		hf = null;
	}
}